﻿namespace MSSqlSchemaDoc.Provider.MicrosoftSqlServer
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using MSSqlSchemaDoc.Core.DataStructures;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Resources;

    /// <summary>
    /// Contains the helper methods for table objects.
    /// </summary>
    public partial class SqlScriptingManager
    {
        /// <summary>
        /// Sets the base data of the trigger object.
        /// </summary>
        /// <param name="trigger">The trigger to populate.</param>
        /// <param name="dt">The data table containing the trigger data.</param>
        /// <param name="currentDtRow">The current row in the data table.</param>
        private void AddTriggerBaseData(TriggerBase trigger, DataTable dt, ref int currentDtRow)
        {
            DataRow row = dt.Rows[currentDtRow];

            trigger.ObjectId = row.Field<int>("ObjectId");
            trigger.Name = row.Field<string>("ObjectName");
            trigger.Schema = row.Field<string>("SchemaName");

            trigger.OperationType = row.Field<string>("OperationType");
            trigger.ImplementationType = row.Field<string>("ImplementationType");
            trigger.Definition = row.Field<string>("Definition");

            trigger.Events = new Collection<TriggerEvent>();

            while (currentDtRow < dt.Rows.Count && trigger.ObjectId == dt.Rows[currentDtRow].Field<int>("ObjectId"))
            {
                trigger.Events.Add(new TriggerEvent(dt.Rows[currentDtRow].Field<string>("EventName")));
                currentDtRow++;
            }
        }

        /// <summary>
        /// Loads the trigger data into the trigger data variable.
        /// </summary>
        /// <param name="connection">The open connection to the database.</param>
        private void LoadTriggerData(SqlConnection connection)
        {
            // Load the triggers - if it's not already loaded
            if (this.triggerData == null)
            {
                // Setup the query
                StringBuilder triggersQuery = new StringBuilder(this.queryManager.Triggers());
                this.InsertWhereClause(triggersQuery);

                // Fetch the data and fill the data table
                DataTable t = new DataTable();
                t.Locale = System.Globalization.CultureInfo.InvariantCulture;
                SqlDataAdapter adapter = new SqlDataAdapter(triggersQuery.ToString(), connection);
                adapter.Fill(t);

                this.triggerData = t;
            }
        }

        /// <summary>
        /// Fetches a list of Ddl triggers and extracts the information for each.
        /// </summary>
        /// <param name="connection">An open connection to the database.</param>
        /// <param name="objectType">The type of object.</param>
        private void ScriptTriggers(SqlConnection connection, DataStructureObjectType objectType)
        {
            // Load trigger data
            this.LoadTriggerData(connection);

            // Initialize Dml trigger collection
            if (objectType == DataStructureObjectType.DmlTrigger)
            {
                this.dmlTriggers = new List<DmlTrigger>();
            }

            // Build each trigger
            int currentRowNumber = 0;
            while (currentRowNumber < this.triggerData.Rows.Count)
            {
                DataRow row = this.triggerData.Rows[currentRowNumber];
                string name = GetObjectName(row);
                TriggerBase currentTrigger = null;

                // Include only the specific type of triggers required
                if (objectType == DataStructureObjectType.DdlTrigger
                    && row.Field<string>("OperationType") == "DDL")
                {
                    this.OnBeginScriptingObject(name);

                    currentTrigger = new DdlTrigger();

                    // Add base data - there's no additional data for DDL triggers
                    this.AddTriggerBaseData(currentTrigger, this.triggerData, ref currentRowNumber);

                    // Documentation
                    if (this.IncludeDocumentationObjects)
                    {
                        currentTrigger.Documentation = this.ConstructDocumentationObject<DdlTriggerDocumentation>(currentTrigger.Name, currentTrigger.Schema);
                    }
                }
                else if (objectType == DataStructureObjectType.DmlTrigger
                    && row.Field<string>("OperationType") == "DML")
                {
                    this.OnBeginScriptingObject(name);

                    DmlTrigger t = new DmlTrigger();

                    // Add base data
                    this.AddTriggerBaseData(t, this.triggerData, ref currentRowNumber);

                    // Add additional data for DML triggers
                    t.ParentId = row.Field<int>("TableId");
                    t.IsInsteadOf = row.Field<bool>("IsInsteadOfTrigger");

                    // Documentation
                    if (this.IncludeDocumentationObjects)
                    {
                        t.Documentation = this.ConstructDocumentationObject<DmlTriggerDocumentation>(t.Name, t.Schema);
                    }

                    currentTrigger = t;
                }
                else
                {
                    currentRowNumber++;
                }

                if (currentTrigger != null)
                {
                    this.OnCompleteScriptingObject(currentTrigger, name);
                }
            }
        }
    }
}
